package apiserver

import (
	"context"
	"go-weixin/app/usercenter/rpc/pb/proto/usercenter"
	"go-weixin/app/usercenter/rpc/userrpcclient"
	"go-weixin/common/ctxdata"
	"net/http"
	"strings"
	"time"

	"github.com/zeromicro/go-zero/core/stores/redis"
	"github.com/zeromicro/go-zero/rest/httpx"
	"github.com/zeromicro/x/errors"
)

type Middleware struct {
	UserRpc userrpcclient.UserCenter
}

func NewApiMiddleware(userRpc userrpcclient.UserCenter) *Middleware {
	return &Middleware{
		UserRpc: userRpc,
	}
}

// api服务的认证中间价(处理视图前触发)
func (m *Middleware) AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		url := r.URL.Path
		uid := ctxdata.GetUidFromCtx(r.Context())
		if (url != "/user/login") && (url != "/user/register") && (uid != 1) {
			ctx, cannFunc := context.WithTimeout(context.Background(), 5*time.Second)
			defer cannFunc()
			// 需要user-rpc服务来完成的rbac鉴权
			res, err := m.UserRpc.UserGetPower(ctx, &usercenter.UserGetPowerReq{
				Id: uid,
			})
			if err != nil {
				httpx.ErrorCtx(r.Context(), w, errors.New(400, err.Error()))
				return
			}
			if len(res.PowerList) > 0 {
				for i := 0; i <= len(res.PowerList); i++ {
					// 循环结束抛出异常
					if i == len(res.PowerList) {
						httpx.ErrorCtx(r.Context(), w, errors.New(404, "没有权限请求此路径"))
						return
					}
					// 判断路径是否为/user/*
					power := res.PowerList[i]
					if strings.HasSuffix(power.Url, "/*") {
						if strings.HasPrefix(url, power.Url[0:len(power.Url)-2]) {
							break
						}
					}
					if url == power.Url {
						break
					}
				}
			}
		}
		next(w, r)
	}
}

// redis请求限流
func ReqFlowRestriction(cache *redis.Redis, uid int, key string, timeOut int) error {
	cacheCtx, cacheCanelFunc := context.WithTimeout(context.Background(), 1*time.Second)
	defer cacheCanelFunc()
	if has, err := cache.SetnxCtx(cacheCtx, key, "req"); err != nil {
		return errors.New(7000, "redis-setnx:"+err.Error())
	} else {
		if has {
			// 设置key成功时(添加超时时间,允许请求)
			err = cache.ExpireCtx(cacheCtx, key, timeOut)
			if err != nil {
				return errors.New(7000, "redis-expire:"+err.Error())
			}
		} else {
			// 设置key失败时(检查超时时间,终止请求)
			if t, err := cache.TtlCtx(cacheCtx, key); err != nil {
				return errors.New(7000, "redis-ttl:"+err.Error())
			} else {
				if t == -2 {
					// 不存在key
					return errors.New(7000, "redis-ttl: 不存在key")
				}
				if t == -1 {
					// key没有超时时间
					err = cache.ExpireCtx(cacheCtx, key, timeOut)
					if err != nil {
						return err
					}
				}
			}
			return errors.New(7000, "请求频繁")
		}
	}
	return nil
}
